// file:system/kernel/sem.c
// autor:jiangxinpeng
// time:2021.6.24
// copyright:(C) 2020-2050 by jiangxinpeng,All right are reserved.

#include <os/semaphore.h>
#include <os/sem.h>
#include <os/memcache.h>
#include <os/mutexlock.h>
#include <os/debug.h>
#include <os/safety.h>
#include <sys/lpc.h>
#include <sys/ipc.h>
#include <lib/string.h>
#include <lib/unistd.h>

sem_t *sem_table=NULL;
DEFINE_SEMAPHORE(sem_mutex, 1);

int SemInit()
{
    sem_table = KMemAlloc(sizeof(sem_t) * SEM_NUM_MAX);
    if (!sem_table)
    {
        KPrint(PRINT_ERR "%s: alloc mem for sem table failed!\n");
        return -1;
    }

    for (int i = 0; i < SEM_NUM_MAX; i++)
    {
        sem_table[i].id = i;
        SemaphoreInit(&sem_table[i].semaphore, 0);
        memset(sem_table[i].name, 0, SEM_NAME_LEN);
    }
    KPrint("[sem] sem init ok!\n");
    return 0;
}

static sem_t *SemFindByName(char *name)
{
    sem_t *sem;
    for (int i = 0; i < SEM_NUM_MAX; i++)
    {
        sem = &sem_table[i];
        if (!memcmp(sem->name, name, SEM_NAME_LEN))
            return sem;
    }
    return NULL;
}

static sem_t *SemFindById(uint64_t id)
{
    sem_t *sem;
    for (int i = 0; i < SEM_NUM_MAX; i++)
    {
        sem = &sem_table[i];
        if (sem->id == id && sem->name[0] != '\0')
            return sem;
    }
    return NULL;
}

sem_t *SemAlloc(char *name, int value)
{
    sem_t *sem;
    for (int i = 0; i < SEM_NUM_MAX; i++)
    {
        sem = &sem_table[i];
        if (sem->name[0] == '\0')
        {
            memcpy(sem->name, name, SEM_NAME_LEN);
            sem->name[SEM_NAME_LEN - 1] = '\0';
            SemaphoreInit(&sem->semaphore, value);
            return sem;
        }
    }
    return NULL;
}

int SemFree(sem_t *sem)
{
    memset(sem->name, 0, SEM_NAME_LEN);
    return 0;
}

int SemGet(char *name, int value, int flags)
{
    int create_new;
    int ret = -1;
    sem_t *sem;

    if (!name||!name[0])
        return -1;
    SemaphoreDown(&sem_mutex);
    if (flags & IPC_CREATE)
    {
        if (flags & IPC_EXCL)
        {
            create_new = -1;
        }
        sem = SemFindByName(name);
        if (sem)
        {
            if (create_new)
                goto err;
            ret = sem->id;
        }
        else
        {
            sem = SemAlloc(name, value);
            if (!sem)
                goto err;
            KPrint(PRINT_ERR "SemGet: alloc new sem id=%d \n", sem->id);
            ret = sem->id;
        }
    }
err:
    SemaphoreUp(&sem_mutex);
    return ret;
}

int SemPut(int id)
{
    sem_t *sem;
    SemaphoreDown(&sem_mutex);
    sem = SemFindById(id);
    if (sem)
    {
        SemFree(sem);
        SemaphoreUp(&sem_mutex);
        return 0;
    }
    SemaphoreUp(&sem_mutex);
    return -1;
}

int SemDown(int id, int flags)
{
    sem_t *sem;
    SemaphoreDown(&sem_mutex);
    sem = SemFindById(id);
    SemaphoreUp(&sem_mutex);
    if (!sem)
    {
        return -1;
    }
    if (flags & IPC_NOWAIT)
    {
        if (SemaphoreTryDown(&sem->semaphore))
            return -1;
    }
    else
        SemaphoreDown(&sem->semaphore);
}

int SemUp(int id)
{
    sem_t *sem;
    SemaphoreDown(&sem_mutex);
    sem = SemFindById(id);
    SemaphoreUp(&sem_mutex);
    if (!sem)
        return -1;
    SemaphoreUp(&sem->semaphore);
}

int SysSemGet(char *name, int value, int flags)
{
    if (!name)
        return -1;
    if (SafetyCheckRange(name, SEM_NAME_LEN))
        return -1;
    return SemGet(name, value, flags);
}

int SysSemPut(uint64_t id)
{
    return SemPut(id);
}

int SysSemDown(int id, int flags)
{

    return SemDown(id, flags);
}

int SysSemUp(uint64_t id)
{
    return SemUp(id);
}